/******************************************************************************
秋夜静，独坐对残灯。
啼笑非非谁识我，坐行梦梦尽缘君，何所慰消沉。
风卷雨，雨复卷侬心。
心似欲随风雨去，茫茫大海任浮沉。
无爱亦无恨。
******************************************************************************/

#ifndef _RGB_SLAVE_H_
#define _RGB_SLAVE_H_

#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif

/******************************************************************************
说明:
      1. RGB 控制模块作为从机被动响应;
	  2. 从机不对参数进行掉电保存,需要保存的参数应由主机保存;
      2. 由于 RAM资源限制,单个数据包最长 381B;
      3. 实际需要发送超过 381B数据的应多次通信;
******************************************************************************/
// 错误码定义
#define  RGB_SLAVE_ERR_NONE     0x00   // 数据正常接收
//| 0x5C | 0xFF| 0xFF | 00 |~0x5C|
#define  RGB_SLAVE_ERR_INIT     0xFF   // 需要发送初始化包,设备上电或从睡眠模式退出或未接收到初始化包或发送的初始化包返回错误时对除睡眠意外的任何指令都将返回该错误码；
#define  RGB_SLAVE_ERR_DECODE   0x01   // 解包错误,当接收达到 381字节数据且不能解码出一个完整的数据包时发送该错误码
#define  RGB_SLAVE_ERR_MATRIX   0x02   // 矩阵大小错误,初始化数据包才有矩阵大小(cols & rows)
#define  RGB_SLAVE_ERR_OVER     0x03   // 矩阵溢出,表示读取或写入的数据超出矩阵大小
#define  RGB_SLAVE_ERR_MODE     0x04   // 调用的灯光模式错误,通常表示无此模式
#define  RGB_SLAVE_ERR_SPEED    0x05   // 速度参数越界, 表示速度参数超出了允许的最大范围
#define  RGB_SLAVE_ERR_ARG      0x06   // 以上为列举的其它参数错误
// 协议头部标识定义
#define  RGB_SLAVE_HEAD         0x5C
// 命令字定义
#define  RGB_SLAVE_CMD_ACK      0xFF
#define  RGB_SLAVE_CMD_SLEEP    0x00
#define  RGB_SLAVE_CMD_INIT     0x01
#define  RGB_SLAVE_CMD_CTRL     0x02
#define  RGB_SLAVE_CMD_UDEF     0x03
#define  RGB_SLAVE_CMD_RECORD   0x04
#define  RGB_SLAVE_CMD_DIRECT   0x05
#define  RGB_SLAVE_CMD_READ     0x06
#define  RGB_SLAVE_CMD_TEST     0x07
#define  RGB_SLAVE_CMD_DIRECT1  0x08
#define  RGB_SLAVE_CMD_DIRECT2  0x09
#define  RGB_SLAVE_CMD_DIRECT3  0x0A

// 键盘矩阵行数定义
#define  RGB_SLAVE_KB_ROWS      6
#define  RGB_SLAVE_KB_COLS      21
// 调色板颜色数量定义
#define  RGB_SLAVE_PALETTE      8

/*
数据包格式
| 0x5C | 0xFF|  1B  |  1B  |  1B   |~0x5C| 6B
| head | cmd | _cmd | code | check | end |
*/
struct RGB_Slave_ack{
    //uint8_t head;   // 0x5C
    //uint8_t cmd;    // 0xFF
    uint8_t _cmd;     // 应答的指令
    uint8_t code;     // 错误码, 0x00:正常, 0xFF:上电(上电主动发送该应答错误码直到接收到初始化数据)
    uint8_t check;    // 校验码, 将接收到的完整数据包累加值保留 8 bit返回给主机进行简单校验
    //uint8_t end;    // ~0x5C
};  // 应答数据
/*
数据包格式
| 0x5C | 0x01|  21  |  6   |  1B    |  1B  |  4B  |  24B |   32B   |~0x5C| 67B
| head | cmd | cols | rows | IdxLogo| RevX | RevY | masks| palettes| end |
*/
struct RGB_Slave_init{
    //uint8_t head;   // 0x5C
    //uint8_t cmd;    // 0x01
    uint8_t cols;
    uint8_t rows;     // rows最高位 bit7为1表示纵向扫描
    // Logo灯在矩阵中的索引号, 0xFF表示 Logo灯为独立于矩阵之外(单总线时 Logo灯独立于矩阵之外应为第一个灯)
    // IdxLogo值不为 0xFF且不在矩阵中时表示不使用 Logo灯
    uint8_t IdxLogo;
    uint8_t reverseX;  // X坐标反向,即行反向, bit0表示第0行是否反向(1反向)，共 6行
    uint32_t reverseY; // Y坐标反向,即列反向, bit0表示第0列是否反向(1反向), 共21列
    uint32_t masks[RGB_SLAVE_KB_ROWS];      // 键位掩码, Bit Map数据,每3字节表示一行([R0,R7],[R8,R15],[R16,R23]),每 bit表示对应键位上是否存在按键, 0:键位空;1:有按键;
    uint32_t palettes[RGB_SLAVE_PALETTE];   // 调色板, palette[0]应为 0表示 LED熄灭
    //uint8_t end;    // ~0x5C
};   // 初始化数据包
/*
数据包格式
| 0x5C | 0x02|  1B  |  1B   | 1B  | 1B  |  4B  |  4B  |  24B |  1B  |~0x5C| 40B
| head | cmd | Mode | Speed | Rev | Gray| back | logo | keys | norm | end |
*/
struct RGB_Slave_control{
    //uint8_t head;   // 0x5C
    //uint8_t cmd;    // 0x02
    uint8_t IdxMode;  // 0xFF,表示模式预览
    uint8_t Speed;
    uint8_t reverse;
    uint8_t Grayscale;
    uint32_t background; // rgb
    uint32_t logo; // rgb
    uint32_t keys[RGB_SLAVE_KB_ROWS];   // 按键状态, Bit Map数据,每3字节表示一行([R0,R7],[R8,R15],[R16,R23]),每 bit表示对应键位上按键状态, 0:无按键按下;1:按键按下;
    uint8_t normally;
    //uint8_t end;      // ~0x5C
};   // 控制指令
/*
数据包格式
| 0x5C | 0x07|  1B |  1B |  1B    |  1B  |  4B  |  24B |  4B   |~0x5C| 39B
| head | cmd | col | row | IdxLogo| RevX | RevY | masks| color | end |
*/
struct RGB_Slave_test{
    //uint8_t head;   // 0x5C
    //uint8_t cmd;    // 0x01
    uint8_t col;
    uint8_t row;
    // Logo灯在矩阵中的索引号, 0xFF表示 Logo灯为独立于矩阵之外(单总线时 Logo灯独立于矩阵之外应为第一个灯)
    // IdxLogo值不为 0xFF且不在矩阵中时表示不使用 Logo灯
    uint8_t IdxLogo;
    uint8_t reverseX;  // 行反向, bit0表示第0行是否反向(1反向)，共 6列
    uint32_t reverseY; // 列反向, bit0表示第0列是否反向(1反向), 共21列
    uint32_t masks[RGB_SLAVE_KB_ROWS];      // 键位掩码, Bit Map数据,每3字节表示一行([R0,R7],[R8,R15],[R16,R23]),每 bit表示对应键位上是否存在按键, 0:键位空;1:有按键;
    uint32_t color;   // 显示的颜色
    //uint8_t end;    // ~0x5C
};   // 测试数据包,在调试阶段使用,用于确定矩阵扫描方向和 键位掩码 数据是否正确
union RGB_Slave_t {
    uint8_t buf[381];
	/*
	数据包格式
	| 0x5C | cmd | data |~0x5C|
	| head | cmd | data | end |
	*/
	struct {
		uint8_t head;   // 0x5C
		uint8_t cmd;
		// uint8_t data[64];
		uint8_t end;    // ~0x5C
	}general;  // 通用指令
	struct {
		uint8_t head;   // 0x5C
		uint8_t cmd;
        uint8_t end;    // ~0x5C
		union {
            struct RGB_Slave_ack ack;  // 应答数据
			/*
			数据包格式
            | 0x5C | 0x00|~0x5C| 3B
			| head | cmd | end |
			*/
			/*struct {
				//uint8_t head;   // 0x5C
				//uint8_t cmd;    // 0x00
				//uint8_t end;    // ~0x5C
			}sleep;*/   // 休眠指令
            struct RGB_Slave_init init;   // 初始化数据包
            struct RGB_Slave_control control;   // 控制指令
			/*
			数据包格式
            | 0x5C | 0x03|126B |~0x5C| 129B
            | head | cmd | rgb | end |
			0x03:自定义(调色板中颜色索引0-6);
			0x04:自定义录制(调色板中颜色索引0-6);
			*/
            /*struct {
				//uint8_t head;   // 0x5C
				//uint8_t cmd;    // 0x03
                // 6*21矩阵,写入的是颜色编号
                // record:6*21矩阵,读取的是颜色编号
                uint8_t rgb[RGB_SLAVE_KB_ROWS*RGB_SLAVE_KB_COLS];  // 颜色编号
				//uint8_t end;    // ~0x5C
            }transfer;*/  //传输包
            uint8_t transfer_rgb[RGB_SLAVE_KB_ROWS*RGB_SLAVE_KB_COLS];  // 颜色编号
            /*
            数据包格式
            | 0x5C | 0x05|378B |~0x5C| 381B
            | head | cmd | rgb | end |
            */
            /*struct {
                //uint8_t head;   // 0x5C
                //uint8_t cmd;    // 0x03
                uint32_t rgb[RGB_SLAVE_KB_ROWS*RGB_SLAVE_KB_COLS];  // RGB颜色
                //uint8_t end;    // ~0x5C
            }direct;*/  //直驱包
            uint32_t direct_rgb[RGB_SLAVE_KB_ROWS*RGB_SLAVE_KB_COLS];  // RGB颜色
            /*
            数据包格式
            | 0x5C | 0x06|~0x5C| 3B
            | head | cmd | end |
            */
            /*struct {
				//uint8_t head;   // 0x5C
				//uint8_t cmd;    // 0x06
				uint8_t offset;   // 灯的偏移
				//uint8_t end;    // ~0x5C
            }read;*/  //传输包
            struct RGB_Slave_test test;   // 测试数据包,在调试阶段使用,用于确定矩阵扫描方向和 键位掩码 数据是否正确
		};
	};  
};
//
union RGB_Slave_Shadow_t {
    uint8_t buf[66];
#if 0
    struct {
//        uint8_t head;   // 0x5C
//        uint8_t cmd;
//        uint8_t end;    // ~0x5C
        union {
            struct RGB_Slave_ack ack;  // 应答数据
            struct RGB_Slave_init init;   // 初始化数据包
            struct RGB_Slave_control control;   // 控制指令
            struct RGB_Slave_test test;   // 测试数据包,在调试阶段使用,用于确定矩阵扫描方向和 键位掩码 数据是否正确
        };
    };
#else
    struct RGB_Slave_ack ack;  // 应答数据
    struct RGB_Slave_init init;   // 初始化数据包
    struct RGB_Slave_control ctrl;   // 控制指令
    struct RGB_Slave_test test;   // 测试数据包,在调试阶段使用,用于确定矩阵扫描方向和 键位掩码 数据是否正确
#endif
};

extern int rgb_slave_ack(uint8_t buf[384], const uint8_t _cmd, const uint8_t code, const uint8_t check);
extern int rgb_slave_ack_decode(const uint8_t buf[], const uint16_t len, struct RGB_Slave_ack *const ack);
extern int rgb_slave_sleep(uint8_t buf[384]);
extern int rgb_slave_init(uint8_t buf[], const uint8_t cols, const uint8_t rows, const uint8_t IdxLogo, const uint8_t reverseX, const uint32_t reverseY, const uint32_t masks[RGB_SLAVE_KB_ROWS], const uint32_t palettes[8]);
extern int rgb_slave_init_decode(const uint8_t buf[], const uint16_t len, struct RGB_Slave_init *const init);
extern int rgb_slave_ctrl(uint8_t buf[384], const uint8_t IdxMode, const uint8_t Speed, const uint8_t reverse, const uint8_t Grayscale, const uint32_t background, const uint32_t logo, const uint32_t keys[RGB_SLAVE_KB_ROWS], const uint8_t normally);
extern int rgb_slave_ctrl_decode(const uint8_t buf[], const uint16_t len, struct RGB_Slave_control *const ctrl);
extern int rgb_slave_udef(uint8_t buf[384], const uint8_t IdxRGB[RGB_SLAVE_KB_ROWS*RGB_SLAVE_KB_COLS]);
extern int rgb_slave_record(uint8_t buf[384], const uint8_t IdxRGB[RGB_SLAVE_KB_ROWS*RGB_SLAVE_KB_COLS]);
extern int rgb_slave_direct(uint8_t buf[384], const uint32_t rgb[RGB_SLAVE_KB_ROWS*RGB_SLAVE_KB_COLS]);
extern int rgb_slave_direct_sub(uint8_t buf[384], const uint8_t direct, const uint32_t rgb[RGB_SLAVE_KB_ROWS/3*RGB_SLAVE_KB_COLS]);
extern int rgb_slave_read(uint8_t buf[384]);
extern int rgb_slave_test(uint8_t buf[384], const uint8_t col, const uint8_t row, const uint8_t IdxLogo, const uint8_t reverseX, const uint32_t reverseY, const uint32_t masks[RGB_SLAVE_KB_ROWS], const uint32_t color);
extern int rgb_slave_test_decode(const uint8_t buf[], const uint16_t len, struct RGB_Slave_test *const test);
// 因 union RGB_Slave_t 中的直驱包需要大缓存，因此在  中不建议直接使用编码和解码函数,以免 RAM 不够用
// 通用编码函数,对数据进行编码,并返回编码后的数据长度
extern int rgb_slave_encode(uint8_t buf[384], union RGB_Slave_t *slave);
// 通用解码函数,对数据进行解码,并返回解码的数据长度
extern int rgb_slave_decode(const uint8_t buf[384], const uint16_t len, union RGB_Slave_t *slave);
// 通用包校验函数,对数据包进行校验,并返回正常校验的数据长度
extern int rgb_slave_check(const uint8_t buf[384], const uint16_t len);
extern int RGB_Slave(uint8_t buf[384], const uint16_t RxLen);

//extern const uint8_t rgb_slave_head[RGB_SLAVE_CMD_TEST+2][3];

#ifdef __cplusplus
}
#endif

#endif  /* _RGB_SLAVE_H_ */
